package com.foreveross.crawl.adapter.sub.impl20140402.v3;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.util.EntityUtils;

import com.foreveross.crawl.adapter.AbstractAdapter;
import com.foreveross.crawl.adapter.PlaneInfoEntityBuilder;
import com.foreveross.crawl.common.util.RegHtmlUtil;
import com.foreveross.crawl.domain.airfreight.AbstractPlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.CabinEntity;
import com.foreveross.crawl.domain.airfreight.TransitEntity;
import com.foreveross.crawl.domain.airfreight.doub.CabinRelationEntity;
import com.foreveross.crawl.domain.airfreight.doub.DoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnCabinEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnDoublePlaneInfoEntity;
import com.foreveross.crawl.domain.airfreight.doub.ReturnTransitEntity;
import com.foreveross.crawl.exception.FlightInfoNotFoundException;
import com.foreveross.taskservice.common.bean.TaskModel;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

/**
 * 阿联酋航空公司手机网站适配器 。
 * 
 * @author luomingliang@foreveross.com
 * @date 2014.07.04
 * @version 1.0.0
 */
public class EmiratesAdapter extends AbstractAdapter {

	private final static int cabin_retry_count = 5; // 仓位信息抓取失败重试次数
	private final static int detail_retry_count = 3; // 详细数据加载失败重试次数

	private final static boolean load_more_cabin_e = true; // 是否加载所有舱位，false 只加载经济舱。

	// 详细信息地址
	private final static String flightUrl = "http://mobile1.emirates.com/english/CAB/IBE/searchResults.xhtml";
	private final static String detail_url = "http://mobile1.emirates.com/english/CAB/IBE/undefined/MAB/searchByPrice/loadSearchByPriceDetails.xhtml?index=%s";

	public EmiratesAdapter(TaskModel taskQueue) {
		super(taskQueue);
	}

	@Override
	public String getUrl() throws Exception {
		return null;
	}

	@Override
	public Object fetch(String url) throws Exception {
		switch (super.getRouteType()) {
		case INTERNATIONAL_ONEWAY:
			return (List<Object>) fetchInterOneWay();
		case INTERNATIONAL_ROUND:
			return (List<Object>) fetchInterRound();
		default:
			return null;
		}
	}

	@Override
	public List<Object> paraseToVo(Object obj) throws Exception {
		List<Object> objs = (List<Object>) obj;
		// printJson(objs); //输出Json数据
		return objs;
	}

	@Override
	public boolean validateFetch(Object obj) throws Exception {
		return true;
	}

	/**
	 * ******** FETCHDATA *************************************************************************************
	 * */

	/** 获取单个仓位的所有航班数量。 */
	private Integer fetchPageDatas(Integer cabinType) throws Exception {
		HttpRequestBase request = null;
		String page = null;
		Integer totalSize = 0;
		try {
			// 重试进行抓取
			for (int i = 0; i < cabin_retry_count; i++) {
				try {
					request = new HttpPost(getFetchUrl(cabinType));
					page = super.excuteRequest(getHttpClient(), request, true);
					super.setLenghtCount(page.length());
					totalSize = Integer.parseInt(RegHtmlUtil.regStr(page, regexMap.get("reg_totalcount"), 1));
					logger.info(String.format("舱位[%s]加载成功，数量[%s]", CABIN.get(cabinType), totalSize));
					break;
				} catch (Exception e) {
					logger.error(String.format("舱位[%s]信息加载失败，进行第[%s]次重试", CABIN.get(cabinType), i + 1));
					rollbackProxyIp(false);
					switchProxyipByHttClient();
				}
			}
		} catch (Exception e) {
			throw e;
		} finally {
			request = null;
			page = null;
		}
		rollbackProxyIp(true);
		return totalSize; // 重试之后还是不能成功
	}

	/** 获取URL */
	private String getFetchUrl(Integer cabinType) {
		StringBuffer url = new StringBuffer();
		String[] goDps = null;
		String[] reDps = null;
		try {
			goDps = taskQueue.getFlightDate().split("-");

			url.append(flightUrl);
			url.append("?totalAdults=1&totalChildren=0&totalInfants=0&sortByType=0&isInterlineFlag=false&maxCabinClassMulti=0");
			url.append("&fromCity=" + taskQueue.getFromCity());
			url.append("&toCity=" + taskQueue.getToCity());
			url.append("&departDay=" + goDps[2]);
			url.append("&departMonth=" + goDps[1]);
			url.append("&departYear=" + goDps[0]);
			url.append("&classType=" + cabinType);

			// 单程
			if (taskQueue.getIsReturn() == 0) {
				url.append("&classTypeRadioOneWay=" + cabinType);
				url.append("&searchType=ON");

				// 往返
			} else {
				reDps = taskQueue.getReturnGrabDate().split("-");
				url.append("&returnDay=".concat(reDps[2]));
				url.append("&returnMonth=".concat(reDps[1]));
				url.append("&returnYear=".concat(reDps[0]));
				url.append("&classTypeRadioReturn=" + cabinType);
				url.append("&sortByTypeRadioMiles=SF");
				url.append("&sortByTypeRadioReturn=0");
				url.append("&flexiDatePage=false");
				url.append("&searchType=RE");
			}
		} finally {
			goDps = null;
			reDps = null;
		}
		return url.toString();
	}

	/** 获取航班的详细数据。 */
	private String fetchDetailData(int index) throws Exception {
		HttpRequestBase request = null;
		String page = null;
		try {
			for (int i = 0; i < detail_retry_count; i++) {
				try {
					request = new HttpPost(String.format(detail_url, index));
					page = super.excuteRequest(getHttpClient(), request, true);
					super.setLenghtCount(page.length());
					break; // 获取成功，退出循环
				} catch (Exception e) {
					logger.error(String.format("加载详细页面[%s]失败：%s,现在进行第[%s]次重试", index, e.getMessage(), i + 1));
				}
			}
		} finally {
			request = null;
		}
		return page;
	}

	/**
	 * ******** ONEWAY *************************************************************************************
	 * */
	/** 国际单程。 */
	private Object fetchInterOneWay() throws Exception {
		List<AbstractPlaneInfoEntity> results = Lists.newArrayList();
		Map<String, AbstractPlaneInfoEntity> map = Maps.newConcurrentMap();

		// 分别获取三个仓位的信息 0:经济 1：商务 2:头等。
		for (int cabinIndex : CABIN.keySet()) {

			// 只加载最低价格的舱位E舱
			if (!load_more_cabin_e) {
				if (cabinIndex >= 0) break;
			}

			// 获取航班列表
			Integer totalSize = this.fetchPageDatas(cabinIndex);
			for (int i = 0; i < totalSize; i++) {
				String detailString = fetchDetailData(i + 1);
				AbstractPlaneInfoEntity t = null;

				// 获取价格
				List<String> prices = RegHtmlUtil.regStrs(detailString, regexMap.get("reg_detail_price"));
				String price = prices.get(1); // 价格
				String total = prices.get(4); // 总计
				String tax = (PlaneInfoEntityBuilder.getDouble(prices.get(2)) + PlaneInfoEntityBuilder.getDouble(prices.get(3))) + ""; // 税费

				List<String> details = RegHtmlUtil.retrieveLinks(detailString, regexMap.get("reg_detail_list"), 1);
				List<TransitEntity> transits = Lists.newArrayList();
				String stopTime = RegHtmlUtil.regStr(detailString, regexMap.get("reg_detail_staytime"));
				String transitNames = getPlaneTransits(getStayTime(stopTime), details, transits, false);

				// 用Map过滤相同航班
				t = map.get(transitNames);
				if (t == null) {
					t = getDetailInfo(details.get(0), false);
					PlaneInfoEntityBuilder.getSingleEntity(t).getTransits().addAll(transits);
					map.put(transitNames, t);
					logger.info(String.format("parse:%s", transitNames));
				}
				String cabinName = RegHtmlUtil.regStr(details.get(0), regexMap.get("reg_detail_cabin"), 1);
				CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, tax, price, total, null, null, CabinEntity.class);
				PlaneInfoEntityBuilder.getSingleEntity(t).getCabins().add(cabin);
				t.setEndTime(transits.get(transits.size() - 1).getEndTime());
			}
		}
		results = new ArrayList<AbstractPlaneInfoEntity>(map.values());
		PlaneInfoEntityBuilder.buildLimitPrice(results);
		return results;
	}

	/**
	 * ******** ROUND *************************************************************************************
	 * */

	/** 国际往返。 */
	private Object fetchInterRound() throws Exception {
		List<AbstractPlaneInfoEntity> results = Lists.newArrayList();
		Map<String, AbstractPlaneInfoEntity> map = Maps.newConcurrentMap();
		Map<String, Map<String, AbstractPlaneInfoEntity>> relationMaps = Maps.newConcurrentMap();
		Map<String, AbstractPlaneInfoEntity> returnMap = null;

		// 分别获取三个仓位的信息 0:经济 1：商务 2:头等。
		for (int cabinIndex : CABIN.keySet()) {

			logger.info(String.format("正在抓取舱位:%s", CABIN.get(cabinIndex)));

			// 只加载最低价格的舱位E舱
			if (!load_more_cabin_e) if (cabinIndex > 0) break;

			// 获取航班列表
			Integer totalSize = this.fetchPageDatas(cabinIndex);
			for (int i = 0; i < totalSize; i++) {
				try {
					String detailString = fetchDetailData(i + 1);

					// 开始解析数据
					AbstractPlaneInfoEntity t = null;

					String goString = RegHtmlUtil.regStr(detailString, regexMap.get("reg_detail_relist"), 1);
					List<String> godetails = RegHtmlUtil.retrieveLinks(goString, regexMap.get("reg_detail_list"), 1);
					String reString = RegHtmlUtil.regStr(detailString, regexMap.get("reg_detail_relist"), 2);
					List<String> redetails = RegHtmlUtil.retrieveLinks(reString, regexMap.get("reg_detail_list"), 1);

					List<TransitEntity> transits = Lists.newArrayList();
					String gostopTime = RegHtmlUtil.regStr(goString, regexMap.get("reg_detail_staytime"));
					String gotransitNames = getPlaneTransits(getStayTime(gostopTime), godetails, transits, false);

					// 用Map过滤相同航班
					t = map.get(gotransitNames);
					if (t == null) {
						t = getDetailInfo(godetails.get(0), false);
						PlaneInfoEntityBuilder.getDoubleEntity(t).getTransits().addAll(transits);
						map.put(gotransitNames, t);
						logger.info(String.format("parse:%s", gotransitNames));
					}

					// 获取价格 1 价格 4总计
					List<String> prices = RegHtmlUtil.regStrs(detailString, regexMap.get("reg_detail_price"));
					String price = prices.get(1); // 价格
					String total = prices.get(4); // 总计
					String tax = (PlaneInfoEntityBuilder.getDouble(prices.get(2)) + PlaneInfoEntityBuilder.getDouble(prices.get(3))) + ""; // 税费

					String cabinName = RegHtmlUtil.regStr(godetails.get(0), regexMap.get("reg_detail_cabin"), 1);
					CabinEntity cabin = PlaneInfoEntityBuilder.buildCabinInfo(cabinName, null, cabinName, null, null, null, null, null, CabinEntity.class);
					PlaneInfoEntityBuilder.getDoubleEntity(t).getCabins().add(cabin);
					t.setStartTime(transits.get(0).getStartTime());
					t.setEndTime(transits.get(transits.size() - 1).getEndTime());

					// RETURN-----------------------------
					AbstractPlaneInfoEntity r = null;
					List<ReturnTransitEntity> returnTransits = Lists.newArrayList();
					String restopTime = RegHtmlUtil.regStr(reString, regexMap.get("reg_detail_staytime"));
					String retransitNames = getPlaneTransits(getStayTime(restopTime), redetails, returnTransits, true);

					// 保存对应的回程集合。
					returnMap = relationMaps.get(gotransitNames);
					if (returnMap == null) {
						returnMap = Maps.newConcurrentMap();
						relationMaps.put(gotransitNames, returnMap);
					}
					r = returnMap.get(retransitNames);
					if (r == null) {
						r = getDetailInfo(redetails.get(0), true);
						PlaneInfoEntityBuilder.getReturnDoubleEntity(r).getReturnTransits().addAll(returnTransits);
						returnMap.put(retransitNames, PlaneInfoEntityBuilder.getReturnDoubleEntity(r));
						logger.info(String.format("return:%s", retransitNames));
					}
					String reCabinName = RegHtmlUtil.regStr(redetails.get(0), regexMap.get("reg_detail_cabin"), 1);
					ReturnCabinEntity reCabin = PlaneInfoEntityBuilder.buildCabinInfo(reCabinName, null, reCabinName, null, null, null, null, null, ReturnCabinEntity.class);
					PlaneInfoEntityBuilder.getReturnDoubleEntity(r).getReturnCabins().add(reCabin);
					r.setStartTime(returnTransits.get(0).getStartTime());
					r.setEndTime(returnTransits.get(returnTransits.size() - 1).getEndTime());

					setRelation(cabin, reCabin, tax, price, total, PlaneInfoEntityBuilder.getDoubleEntity(t));

				} catch (Exception e) {
					logger.error(String.format("舱位[%s - %s]加载失败。", CABIN.get(cabinIndex), i));
				} finally {

				}
			}
		}
		for (String key : map.keySet()) {
			Map<String, AbstractPlaneInfoEntity> rd = relationMaps.get(key);
			logger.info(String.format("%s returns:%s", key, rd.size()));
			for (AbstractPlaneInfoEntity rt : rd.values()) {
				PlaneInfoEntityBuilder.getDoubleEntity(map.get(key)).getReturnPlaneInfos().add((ReturnDoublePlaneInfoEntity) rt);
			}
		}
		logger.info(String.format("抓取完成:%s", map.size()));
		results = new ArrayList<AbstractPlaneInfoEntity>(map.values());
		PlaneInfoEntityBuilder.buildLimitPrice(results); // 设置最高低价。
		return results;
	}

	/** 设置航班详细信息 。 */
	private AbstractPlaneInfoEntity getDetailInfo(String detailStr, Boolean isReturn) throws Exception {
		String flightNo, type;
		AbstractPlaneInfoEntity t = null;
		flightNo = RegHtmlUtil.regStr(detailStr, regexMap.get("reg_detail_flightNo"), 1);
		type = RegHtmlUtil.regStr(detailStr, regexMap.get("reg_detail_type"), 1);
		if (!isReturn) {
			t = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, null, null, flightNo, null, "阿联酋", null, type);
		} else {
			t = PlaneInfoEntityBuilder.buildPlaneInfo(taskQueue, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, null, null, flightNo, null, "阿联酋", null, type, ReturnDoublePlaneInfoEntity.class);
		}
		return t;
	}

	/** 设置仓位关系 。 */
	private void setRelation(CabinEntity cabin, ReturnCabinEntity reCabin, String tax, String price, String totalprice, DoublePlaneInfoEntity t) {
		CabinRelationEntity relation = new CabinRelationEntity();
		relation.setCabinId(cabin.getId());
		relation.setReturnCabinId(reCabin.getId());
		relation.setTaxesPrice(PlaneInfoEntityBuilder.getDouble(tax));
		relation.setFullPrice(PlaneInfoEntityBuilder.getDouble(price));
		relation.setTotalFullPrice(PlaneInfoEntityBuilder.getDouble(totalprice));
		t.getCabinRelations().add(relation);
	}

	/** 设置中转信息。 */
	private <T> String getPlaneTransits(Long stayTime, List<String> flightDeports, List<T> transits, Boolean isReturn) throws Exception {

		String flightNo, type, flightNames = "";
		// 保存中转信息。
		for (int i = 0; i < flightDeports.size(); i++) {

			flightNo = RegHtmlUtil.regStr(flightDeports.get(i), "cell\"><p>(EK\\d{3,4})</p></div>", 1);
			// 1 fromName 2from 3toName 4toName
			List<String> arrdeps = RegHtmlUtil.regStrs(flightDeports.get(i), regexMap.get("reg_detail_deparr"));
			// 1ftime 2fdate 3ttime 4tdate
			List<String> times = RegHtmlUtil.regStrs(flightDeports.get(i), regexMap.get("reg_detail_time"));

			type = RegHtmlUtil.regStr(flightDeports.get(i), regexMap.get("reg_detail_type"), 1);

			flightNames += flightNo;

			if (!isReturn) {
				TransitEntity tr = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, flightNo, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, arrdeps.get(2), arrdeps.get(1), arrdeps.get(4), arrdeps.get(3), type, TransitEntity.class);
				tr.setStartTime(getDate(times.get(2) + "/" + times.get(1)));
				tr.setEndTime(getDate(times.get(4) + "/" + times.get(3)));
				tr.setStayTime(stayTime);
				transits.add((T) tr);
			} else {
				ReturnTransitEntity tr = PlaneInfoEntityBuilder.buildTransitEntity(flightNo, flightNo, AIRPORTCODE, AIRPORTNAME, AIRPORTNAME, arrdeps.get(2), arrdeps.get(1), arrdeps.get(4), arrdeps.get(3), type, ReturnTransitEntity.class);
				tr.setStartTime(getDate(times.get(2) + "/" + times.get(1)));
				tr.setEndTime(getDate(times.get(4) + "/" + times.get(3)));
				tr.setStayTime(stayTime);
				transits.add((T) tr);
			}
		}
		return flightNames;
	}

	/** 验证数据 。 */
	public void validateData(Object obj) throws Exception {

		if (obj == null) {
			throw new Exception("阿联酋获取数据为空");
		}
		String page = obj.toString();
		page = page.replaceAll("\\s", "");
		if (page.matches(".*Therearenoflightsavailableforthissearch.*")) {
			throw new FlightInfoNotFoundException("没有符合搜索条件的航班");
		}
		if (page.matches(".*eitherflightsareunavailable.*") || page.matches(".*seatsarenotavailableontheflightsfortheroutes/datesyouhaveselected.*")) {
			throw new FlightInfoNotFoundException();
		}
		if (page.matches(".*Unabletoprocesstherequestduetothefollowingreasons.*")) {
			throw new Exception("无法处理该请求");
		}
	}

	/**
	 * ******** UTILS *************************************************************************************
	 * */

	/** 将 这种样式的Fri 20 Dec 13 / 23:05 转化为时间 */
	private Date getDate(String time) throws ParseException {
		Date d = null;
		String pattern = "EEEddMMMyy/HH:mm";
		String temp;
		temp = time.replaceAll("\\s", "");
		d = new SimpleDateFormat(pattern, Locale.ENGLISH).parse(temp);
		return d;
	}

	/**
	 * ******** PARAMS *************************************************************************************
	 * */

	private final static String AIRPORTNAME = "阿联酋航空";
	private final static String AIRPORTCODE = "EK";

	/** 正则 */
	private final static Map<String, String> regexMap = new HashMap<String, String>() {
		private static final long serialVersionUID = -1030831565062976554L;
		{
			put("reg_loadmore", "id=\"showLoadMoreBtn\"\\s+?value=\"true\"/>");
			put("reg_totalcount", "id=\"totalPageCount\"\\s*?value=\"(\\d*?)\"/>");
			put("reg_list", "<div\\s+?id=\"priceDetailsDiv_.+?class=\"default-accordion\\s*?accordion-container.+?>(.*?)</a>");
			put("reg_detail_flightNo", "cell\"><p>(EK\\d{3,4})</p></div>");
			put("reg_detail_cabin", "stop\\(s\\).*?<p>\\s*?(\\w*?)\\s*?</p>");

			StringBuffer pricereg = new StringBuffer();
			pricereg.append("Fare</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>.*?");
			pricereg.append("Taxes</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>.*?");
			pricereg.append("charges</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>.*?");
			pricereg.append("Cost</p>\\s*?<p\\s*?class=\"text\\s*?text-right\\s*?fs-22\\s*?fl-right\">(.*?)</p>");
			put("reg_detail_price", pricereg.toString());// 票价
			// put("reg_detail_price", "Total\\s+?price.+?<strong>CNY\\s+?(.+?)</strong>");//总价

			put("reg_detail_list", "<div\\s*?id=\"flightDtlsDiv_(.+?)/\\s+?Tab");
			put("reg_detail_relist", "icon-plane-green(.*?)Returning(.*?)Summary");
			put("reg_detail_deparr", "airport\\s*?fs-22\\s*?mb-16\">\\n*\\s+(.*?)\\s*\\(([A-Z]{3})\\).*airport\\s*?fs-22\\s*?mb-16\">\\n*\\s+(.*?)\\s*\\(([A-Z]{3})\\)");
			put("reg_detail_time",
					"<div\\s*?class=\"left\\s*?cell\\s*?terminal\">\\s*?<p><strong>(.+?)</strong></p>\\s*?<p>(.+?)</p>\\s*?</div>.*?<div\\s*?class=\"right\\s*?cell\\s*?terminal\">\\s*?<p><strong>(.+?)</strong></p>\\s*?<p>(.+?)</p>\\s*?</div>");
			put("reg_detail_type", "add-flight-info.+?add-flight-info.+?<p>(.+?)</p>");
			put("reg_detail_staytime", "class=\"connection-box\">\\s*?Connection\\s*(.*?)\\s*?</div>");
		}
	};

	private Long getStayTime(String stayTime) {
		Long staytime = 0L;
		try {
			stayTime = stayTime.trim();
			if (RegHtmlUtil.regMathcher(stayTime, "\\d*?hr\\s*?\\d*?min")) {
				String[] strs = stayTime.split(" ");
				Integer hour = Integer.parseInt(strs[0].replace("hr", ""));
				Integer min = Integer.parseInt(strs[1].replace("min", ""));
				staytime = (hour * 60 * 60 * 1000) + (min * 60 * 1000L);
			}
		} catch (Exception e) {
			staytime = 0L;
			logger.info("中转停留时间解析出错，设置默认值为0.");
		}
		return staytime;
	}

	/** 仓位 */
	private final static Map<Integer, String> CABIN = new HashMap<Integer, String>() {
		private static final long serialVersionUID = -8065352468685942934L;
		{
			put(0, "经济舱");
			put(1, "商务舱");
			put(2, "头等舱");
		}
	};
}
